import { Component, Host, h, ComponentInterface, Element, Listen, Prop, State, writeTask } from '@stencil/core';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { ISearchDecorationOptions, SearchAddon } from 'xterm-addon-search';
// import {} from 'xterm-addon-web-links';
import { WebglAddon } from 'xterm-addon-webgl';
import { Unicode11Addon } from 'xterm-addon-unicode11';
import { ResizeSensor } from 'css-element-queries';
import { XtermThemeCfg } from '../../configs';
import { contextMenu, ContextMenuItem } from '@ibiz/context-menu';
import { ITheme } from 'xterm';
import { TerminalMessageService } from '../../service';
import { TerminalMessageItem } from '../../entities';

/**
 * 终端
 *
 * @author chitanda
 * @date 2021-06-21 17:06:06
 * @export
 * @class IbzTerminal
 */
@Component({
  tag: 'ibz-terminal',
  styleUrl: 'ibz-terminal.scss',
})
export class IBzTerminal implements ComponentInterface {
  @Element()
  el: HTMLDivElement;
  input: HTMLIbzTextCopyElement;

  @Prop()
  ms: TerminalMessageService = TerminalMessageService.getInstance();

  @Prop()
  theme: 'dark' | 'light' = 'dark';

  /**
   * 当前选择主题
   *
   * @author chitanda
   * @date 2021-06-22 18:06:32
   * @type {ITheme}
   */
  xTheme: ITheme;

  /**
   * 是否已经挂载过
   *
   * @author chitanda
   * @date 2021-06-21 19:06:54
   */
  isDidLoad = false;

  /**
   * 终端实例
   *
   * @author chitanda
   * @date 2021-06-21 18:06:44
   * @type {Terminal}
   */
  @Prop()
  terminal: Terminal = new Terminal({
    cols: 100,
    rows: 30,
    theme: XtermThemeCfg.dark,
    allowProposedApi: true,
    allowTransparency: true,
  });

  /**
   * 终端搜索插件
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:14
   * @type {SearchAddon}
   */
  searchAddon: SearchAddon = new SearchAddon();

  /**
   * 搜索工具栏
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:30
   * @type {HTMLDivElement}
   */
  searchBar: HTMLDivElement;

  /**
   * 搜索工具栏输入框
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:42
   * @type {HTMLInputElement}
   */
  searchBarInput: HTMLInputElement;

  /**
   * 是否开启搜索工具栏
   *
   * @author zhanghengfeng
   * @date 2023-07-11 13:07:29
   * @type {boolean}
   */
  @State()
  isOpenSearchBar: boolean = false;

  /**
   * 是否使用正则表达式进行搜索
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:44
   * @type {boolean}
   */
  @State()
  isOpenRegex: boolean = false;

  /**
   * 搜索选中项背景色配置
   *
   * @author zhanghengfeng
   * @date 2023-07-11 14:07:43
   * @type {ISearchDecorationOptions}
   */
  decorations: ISearchDecorationOptions = {
    matchBackground: '#613214',
    matchOverviewRuler: '',
    activeMatchBackground: '#515C6A',
    activeMatchColorOverviewRuler: '',
  };

  /**
   * 终端尺寸适应插件
   *
   * @author chitanda
   * @date 2021-06-21 18:06:50
   * @type {FitAddon}
   */
  fit: FitAddon = new FitAddon();
  /**
   * 采用webgl绘制终端
   *
   * @author chitanda
   * @date 2021-06-21 18:06:17
   * @type {WebglAddon}
   */
  webgl: WebglAddon = new WebglAddon();
  /**
   * 编码版本
   *
   * @author chitanda
   * @date 2021-06-21 19:06:45
   * @type {Unicode11Addon}
   */
  unicode11: Unicode11Addon = new Unicode11Addon();
  /**
   * 组件大小变更监控
   *
   * @author chitanda
   * @date 2021-06-21 19:06:14
   * @type {ResizeSensor}
   */
  resize: ResizeSensor;
  /**
   * 右键菜单项
   *
   * @author chitanda
   * @date 2021-06-22 15:06:23
   * @type {ContextMenuItem[]}
   */
  contextMenus: ContextMenuItem[] = [
    { tag: 'select-all', text: '选择全部' },
    { type: 'separator' },
    { tag: 'clear', text: '清空' },
  ];
  /**
   * 右击事件
   *
   * @author chitanda
   * @date 2021-06-22 16:06:37
   * @param {MouseEvent} e
   */
  @Listen('contextmenu')
  contextmenu(e: MouseEvent): void {
    contextMenu.open(e, this.calcMenus(), { minWidth: 100 }).click(item => {
      this.contextmenuHandler(item);
    });
  }

  /**
   * 根据选中状态计算上下文菜单内容
   *
   * @author chitanda
   * @date 2021-06-22 16:06:20
   * @return {*}  {ContextMenuItem[]}
   */
  calcMenus(): ContextMenuItem[] {
    if (this.terminal.hasSelection()) {
      return [{ tag: 'copy', text: '拷贝' }, ...this.contextMenus];
    }
    return this.contextMenus;
  }

  /**
   * 寻找上一个匹配项
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:02
   */
  findPrevious() {
    this.searchAddon.findPrevious(this.searchBarInput?.value || '', {
      regex: this.isOpenRegex,
      decorations: this.decorations,
    });
  }

  /**
   * 寻找下一个匹配项
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:43
   */
  findNext() {
    this.searchAddon.findNext(this.searchBarInput?.value || '', {
      regex: this.isOpenRegex,
      decorations: this.decorations,
    });
  }

  /**
   * 打开搜索工具栏
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:38
   */
  openSearchBar() {
    this.isOpenSearchBar = true;
    writeTask(() => {
      this.searchBarInput?.focus();
    });
  }

  /**
   * 关闭搜索工具栏
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:13
   */
  closeSearchBar() {
    this.isOpenSearchBar = false;
    if (this.searchBarInput) {
      this.searchBarInput.value = '';
    }
    this.findPrevious();
  }

  /**
   * 监听搜索工具栏输入框keydown事件
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:31
   * @param {KeyboardEvent} e
   */
  onSearchBarInputKeydown(e: KeyboardEvent) {
    if (e.ctrlKey && e.key.toLowerCase() === 'f') {
      e.preventDefault();
    }
  }

  /**
   * 监听搜索工具栏输入框keyup事件
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:59
   * @param {KeyboardEvent} e
   * @return {*}
   */
  onSearchBarInputKeyup(e: KeyboardEvent) {
    if (e.isComposing) {
      return;
    } else if (e.key === 'Escape') {
      this.closeSearchBar();
    } else if (e.key === 'Enter') {
      this.findNext();
    } else {
      this.findPrevious();
    }
  }

  /**
   * 监听搜索工具栏正则表达式按钮点击
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:33
   */
  onSearchBarRegexButtonClick() {
    this.isOpenRegex = !this.isOpenRegex;
    this.searchAddon.clearDecorations();
    this.findPrevious();
    writeTask(() => {
      this.searchBarInput?.focus();
    });
  }

  /**
   * 初始化搜索工具栏
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:22
   */
  initSearchBar() {
    this.onSearchBarInputKeydown = this.onSearchBarInputKeydown.bind(this);
    this.onSearchBarInputKeyup = this.onSearchBarInputKeyup.bind(this);
    this.openSearchBar = this.openSearchBar.bind(this);
    this.closeSearchBar = this.closeSearchBar.bind(this);
    if (this.searchBarInput) {
      this.searchBarInput.addEventListener('keydown', this.onSearchBarInputKeydown);
      this.searchBarInput.addEventListener('keyup', this.onSearchBarInputKeyup);
    }
  }

  componentDidLoad(): void {
    this.isDidLoad = true;
    this.init();
    this.terminal.attachCustomKeyEventHandler(e => {
      if (e.key === 'c' && (e.ctrlKey || e.metaKey) && this.terminal.hasSelection()) {
        this.copy();
        return false;
      }
      if (e.ctrlKey && e.key.toLowerCase() === 'f') {
        this.openSearchBar();
        e.preventDefault();
        return false;
      }
      if (e.key === 'Escape') {
        this.closeSearchBar();
        return false;
      }
      return true;
    });
  }

  connectedCallback(): void {
    this.ms.enable();
    this.ms.evt.on('message', this.messageHandler);
    if (this.isDidLoad) {
      this.init();
    }
  }

  disconnectedCallback(): void {
    this.ms.disable();
    this.ms.evt.off('message', this.messageHandler);
    this.resize?.detach(this.resizeSensorCallback);
    this.terminal.dispose();
    if (this.searchBarInput) {
      this.searchBarInput.removeEventListener('keydown', this.onSearchBarInputKeydown);
      this.searchBarInput.removeEventListener('keyup', this.onSearchBarInputKeyup);
    }
  }

  init(): void {
    this.xTheme = XtermThemeCfg[this.theme];
    this.terminal.options.theme = this.xTheme;
    // 加载terminal插件
    this.loadAddons(this.terminal);
    // 监控元素大小变更
    this.resize = new ResizeSensor(this.el, this.resizeSensorCallback);
    this.terminal.open(this.el);
    this.terminal.loadAddon(this.webgl);
    this.terminal.loadAddon(this.searchAddon);
    this.initSearchBar();
    // 订阅webgl销毁事件
    this.webgl.onContextLoss(() => {
      this.webgl.dispose();
    });
    this.fit.fit();
    this.defaultWrite();
  }

  /**
   * 进行默认输出
   *
   * @author chitanda
   * @date 2021-06-23 11:06:17
   */
  defaultWrite(): void {
    this.ms.readAll().forEach(item => {
      this.messageHandler(item);
    });
    this.terminal.scrollToBottom();
  }

  /**
   * 挂载插件
   *
   * @author chitanda
   * @date 2021-06-21 19:06:46
   * @param {Terminal} t
   */
  loadAddons(t: Terminal): void {
    t.loadAddon(this.fit);
    t.loadAddon(this.unicode11);
    t.unicode.activeVersion = '11';
  }

  /**
   * 消息输出处理
   *
   * @author chitanda
   * @date 2021-06-23 11:06:29
   * @param {TerminalMessageItem} item
   */
  messageHandler = (item: TerminalMessageItem): void => {
    switch (item.writeMode) {
      case 'ln':
        return this.terminal.write(item.msg + '\n\r');
      case 'progress':
        this.terminal.onLineFeed;
        return this.terminal.write('\r' + item.msg);
      default:
        return this.terminal.write(item.msg);
    }
  };

  timer: any = null;
  resizeSensorCallback = (): void => {
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }
    this.timer = setTimeout(() => {
      this.fit.fit();
    }, 100);
  };

  /**
   * 右键菜单点击
   *
   * @author chitanda
   * @date 2021-06-22 15:06:33
   * @param {ContextMenuItem} item
   */
  contextmenuHandler(item: ContextMenuItem): void {
    switch (item.tag) {
      case 'copy':
        return this.copy();
      case 'select-all':
        return this.selectAll();
      case 'clear':
        return this.clear();
    }
  }

  /**
   * 拷贝选中内容
   *
   * @author chitanda
   * @date 2021-06-23 11:06:52
   */
  copy(): void {
    const str = this.terminal.getSelection();
    this.input.copy(str);
  }

  /**
   * 选中所有内容
   *
   * @author chitanda
   * @date 2021-06-23 11:06:59
   */
  selectAll(): void {
    this.terminal.selectAll();
  }

  /**
   * 清空所有内容
   *
   * @author chitanda
   * @date 2021-06-23 11:06:05
   */
  clear(): void {
    this.terminal.clear();
    this.terminal.clearSelection();
    this.ms.clear();
  }

  /**
   * 渲染搜索工具栏
   *
   * @author zhanghengfeng
   * @date 2023-07-10 19:07:54
   * @return {*}
   */
  renderSearchBar() {
    return (
      <div
        class={{ 'ibz-terminal-search-bar': true, 'ibz-terminal-search-bar--close': !this.isOpenSearchBar }}
        ref={ref => (this.searchBar = ref)}
      >
        <div class='ibz-terminal-search-bar-left'>
          <input type='text' class='ibz-terminal-search-bar__input' ref={ref => (this.searchBarInput = ref)} />
          <div
            class={{
              'ibz-terminal-search-bar__regex': true,
              'ibz-terminal-search-bar__regex--selected': this.isOpenRegex,
            }}
            title='使用正则表达式'
            onClick={() => this.onSearchBarRegexButtonClick()}
          >
            <svg
              class='icon'
              viewBox='0 0 1024 1024'
              version='1.1'
              xmlns='http://www.w3.org/2000/svg'
              width='16'
              height='16'
            >
              <path d='M136 836.928c0-32.896 25.088-60.608 58.112-60.608 34.304 0 59.392 27.776 59.392 60.608 0 35.712-25.088 59.456-59.392 59.456-36.992-1.344-58.112-26.432-58.112-59.456zM903.808 398.336l-214.912 42.112 150.336 188.032-92.928 64-120.64-207.744L503.68 692.48l-92.992-64 149.568-188.032-216.32-42.112 37.76-104.576 204.8 78.464-19.648-237.44h116.928l-20.352 237.44 204.8-78.464 35.584 104.576z'></path>
            </svg>
          </div>
        </div>
        <div class='ibz-terminal-search-bar-right'>
          <div class='ibz-terminal-search-bar__button' title='上一个匹配项' onClick={() => this.findPrevious()}>
            <svg
              class='icon'
              viewBox='0 0 1024 1024'
              version='1.1'
              xmlns='http://www.w3.org/2000/svg'
              width='16'
              height='16'
            >
              <path d='M512 927.8464a32 32 0 0 1-32-31.8464V203.3664a32 32 0 0 1 64 0V896a32 32 0 0 1-32 31.8464z'></path>
              <path d='M766.464 458.8032a31.8976 31.8976 0 0 1-22.6304-9.3696L512 217.6 280.1664 449.4336a32 32 0 1 1-45.2608-45.2608L512 127.0784l277.0944 277.0944a32 32 0 0 1-22.6304 54.6304z'></path>
            </svg>
          </div>
          <div class='ibz-terminal-search-bar__button' title='下一个匹配项' onClick={() => this.findNext()}>
            <svg
              class='icon'
              viewBox='0 0 1024 1024'
              version='1.1'
              xmlns='http://www.w3.org/2000/svg'
              width='16'
              height='16'
            >
              <path d='M512 858.7264a32 32 0 0 1-32-32V124.7744a32 32 0 1 1 64 0v701.952a32 32 0 0 1-32 32z'></path>
              <path d='M512 901.7344L234.9056 624.64a32 32 0 1 1 45.2608-45.2608L512 811.2128l231.8336-231.7824a32 32 0 0 1 45.2608 45.2608z'></path>
            </svg>
          </div>
          <div class='ibz-terminal-search-bar__button' title='关闭' onClick={() => this.closeSearchBar()}>
            <svg
              class='icon'
              viewBox='0 0 1024 1024'
              version='1.1'
              xmlns='http://www.w3.org/2000/svg'
              width='16'
              height='16'
            >
              <path d='M821.56316445 851.26144a29.70965333 29.70965333 0 0 1-20.8896-8.66872889L511.99317333 553.91345778 223.32757333 842.58133333a29.42976 29.42976 0 0 1-20.76785778 8.48782222 29.49461333 29.49461333 0 0 1-20.80881777-8.52309333c-5.63882667-5.55690667-8.77909333-12.97863111-8.83712-20.89528889s2.97073778-15.38503111 8.52195555-21.02385778l288.63260445-288.63260444L181.40728889 223.32757333c-11.26627555-11.43580445-11.26627555-30.10104889 0.03413333-41.57667555a29.41155555 29.41155555 0 0 1 21.11715556-8.83712 29.48323555 29.48323555 0 0 1 20.80312889 8.52195555L511.98862222 470.07516445 800.66104889 181.40728889c5.59786667-5.59217778 13.03779555-8.68010667 20.96014222-8.68010667s15.36113778 3.08224 20.96583111 8.68010667a29.70396445 29.70396445 0 0 1-0.00568889 41.92028444L553.91345778 512 842.58133333 800.66673778a29.46503111 29.46503111 0 0 1 8.66759112 20.91918222 29.68689778 29.68689778 0 0 1-29.63342223 29.68120889h-0.05233777z'></path>
            </svg>
          </div>
        </div>
      </div>
    );
  }

  render() {
    return (
      <Host class='ibz-terminal' style={{ '--scrollbar-background-color': XtermThemeCfg.dark.foreground + '7a' }}>
        <ibz-text-copy ref={ref => (this.input = ref)} />
        {this.renderSearchBar()}
      </Host>
    );
  }
}
